﻿using System;

namespace Ooaavee.TestTools
{
    /// <summary>
    /// Used to enrich a test method with extension point features.
    /// </summary>
    /// <remarks>
    /// This attribute can be specified on a class or on a method.
    /// 
    /// If this attribute is specified on a class, the meaning of it will be automatically applied to every test method in that 
    /// same test class.
    /// </remarks>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
    public abstract class TestMethodFeatureBaseAttribute : Attribute
    {
        internal void Initialize(TestBase test)
        {
            test.BeforeTest += BeforeTest;
            test.AfterTest += AfterTest;
            test.AfterPassedTest += AfterPassedTest;
            test.AfterFailedTest += AfterFailedTest;
        }

        /// <summary>
        /// Handles the BeforeTest event.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">A <see cref="TestMethodEventArgs"/>  that contains the event data.</param>
        private void BeforeTest(object sender, TestMethodEventArgs e)
        {
            try
            {
                OnBeforeTest();
            }
            catch (Exception ex)
            {
                HandleAndHideException(ex);                
            }
        }

        /// <summary>
        /// Handles the AfterTest event.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">A <see cref="TestMethodEventArgs"/>  that contains the event data.</param>
        private void AfterTest(object sender, TestMethodEventArgs e)
        {
            try
            {
                OnAfterTest();
            }
            catch (Exception ex)
            {
                HandleAndHideException(ex);                
            }
        }

        /// <summary>
        /// Handles the AfterPassedTest event.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">A <see cref="TestMethodEventArgs"/>  that contains the event data.</param>
        private void AfterPassedTest(object sender, TestMethodEventArgs e)
        {
            try
            {
                OnAfterPassedTest();
            }
            catch(Exception ex)
            {
                HandleAndHideException(ex);                
            }
        }

        /// <summary>
        /// Handles the AfterFailedTest event.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">A <see cref="TestMethodEventArgs"/>  that contains the event data.</param>
        private void AfterFailedTest(object sender, TestMethodEventArgs e)
        {
            try
            {
                OnAfterFailedTest();
            }
            catch (Exception ex)
            {
                HandleAndHideException(ex);
            }
        }

        /// <summary>
        /// Handles and hides an exception...
        /// </summary>
        /// <param name="ex">an exception</param>
        private static void HandleAndHideException(Exception ex)
        {
            // Do nothing now, maybe some logging here in future...
        }        

        /// <summary>
        /// This method will be called before the test method.
        /// </summary>
        protected virtual void OnBeforeTest()
        {
        }

        /// <summary>
        /// This method will be called after the test method.
        /// </summary>
        protected virtual void OnAfterTest()
        {
        }

        /// <summary>
        /// This method will be called after the test method, if the test method was passed.
        /// </summary>
        protected virtual void OnAfterPassedTest()
        {
        }

        /// <summary>
        /// This method will be called after the test method, if the test method was failed.
        /// </summary>
        protected virtual void OnAfterFailedTest()
        {
        }
    }
}
